﻿#include <event2/event.h>          // libevent核心头文件
#include <event2/bufferevent_ssl.h> // libevent的SSL封装
#include <openssl/ssl.h>           // OpenSSL库核心头文件
#include <openssl/err.h>           // OpenSSL错误处理头文件
#include <spdlog/spdlog.h>         // 日志库
#include <spdlog/sinks/basic_file_sink.h> // 文件日志接收器
#include <fstream>                 // 文件操作
#include <iostream>                // 标准输入输出流

// 定义客户端和服务器证书文件的名称
#define CERT_FILE "client.crt"
#define KEY_FILE "client.key"
#define SERVER_CERT_FILE "server.crt"

// 函数：保存对端证书
void save_peer_certificate(SSL* ssl, const std::string& filename) {
	X509* cert = SSL_get_peer_certificate(ssl); // 获取对端证书
	if (cert) {
		BIO* bio = BIO_new_file(filename.c_str(), "w"); // 创建BIO文件写入对象
		PEM_write_bio_X509(bio, cert); // 将证书写入文件
		BIO_free(bio); // 释放BIO对象
		X509_free(cert); // 释放证书对象
		spdlog::info("Saved peer certificate to {}", filename); // 记录日志
	}
	else {
		spdlog::warn("No peer certificate received"); // 记录日志
	}
}

// 函数：加载并验证服务器证书
void load_and_verify_peer_certificate(SSL* ssl, const std::string& filename) {
	BIO* bio = BIO_new_file(filename.c_str(), "r"); // 创建BIO文件读取对象
	if (bio) {
		X509* cert = PEM_read_bio_X509(bio, NULL, 0, NULL); // 从文件读取证书
		if (cert) {
			// 此函数用于加载证书到SSL上下文，但在这里使用可能有误，
			// 因为SSL_CTX_use_certificate是用来在服务器端加载证书的，
			// 客户端通常使用SSL_CTX_load_verify_locations或SSL_add_file_cert_subjects_to_stack
			if (SSL_CTX_use_certificate(SSL_get_SSL_CTX(ssl), cert) == 1) {
				spdlog::info("Loaded and verified peer certificate from {}", filename);
			}
			else {
				spdlog::error("Failed to use peer certificate from {}", filename);
			}
			X509_free(cert);
		}
		else {
			spdlog::error("Failed to read peer certificate from {}", filename);
		}
		BIO_free(bio);
	}
	else {
		spdlog::error("Failed to open peer certificate file: {}", filename);
	}
}

// 读回调函数：处理从服务器接收的数据
void read_cb(struct bufferevent* bev, void* ctx) {
	char msg[512]; // 接收数据的缓冲区
	int n;

	SSL* ssl = bufferevent_openssl_get_ssl(bev); // 获取关联的SSL对象
	save_peer_certificate(ssl, SERVER_CERT_FILE); // 保存服务器证书
	load_and_verify_peer_certificate(ssl, SERVER_CERT_FILE); // 加载并验证服务器证书

	// 循环读取数据直到没有更多数据
	while ((n = bufferevent_read(bev, msg, sizeof(msg))) > 0) {
		spdlog::info("Received message: {}", std::string(msg, n)); // 记录日志
		fwrite(msg, 1, n, stdout); // 将接收到的数据写入标准输出
	}
}

// 事件回调函数：处理连接事件和错误事件
void event_cb(struct bufferevent* bev, short events, void* ctx) {
	if (events & BEV_EVENT_CONNECTED) { // 如果连接成功
		spdlog::info("Connected to server"); // 记录日志
		const char* message = "Hello, Server!"; // 要发送的消息
		bufferevent_write(bev, message, strlen(message)); // 发送消息
	}
	else if (events & BEV_EVENT_ERROR) { // 如果发生错误
		SSL* ssl = bufferevent_openssl_get_ssl(bev); // 获取关联的SSL对象
		save_peer_certificate(ssl, SERVER_CERT_FILE); // 保存服务器证书
		unsigned long err;
		// 循环获取并打印SSL错误
		while ((err = (bufferevent_get_openssl_error(bev)))) {
			spdlog::error("SSL error: {}", ERR_reason_error_string(err));
		}
	}
	// 如果连接结束或发生错误，释放bufferevent资源
	if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
		spdlog::info("Connection closed"); // 记录日志
		bufferevent_free(bev);
	}
}

int main() {
	// 设置日志级别和文件日志
	spdlog::set_level(spdlog::level::info);
	auto file_logger = spdlog::basic_logger_mt("file_logger", "client_log.txt");
#ifdef _WIN32
	// 加载套接字库
	WSADATA wsaData;
	int iRet = 0;
	iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iRet != 0)
	{
		std::cout << "WSAStartup(MAKEWORD(2, 2), &wsaData) execute failed!" << std::endl;
		return -1;
	}
	if (2 != LOBYTE(wsaData.wVersion) || 2 != HIBYTE(wsaData.wVersion))
	{
		std::cout << "WSADATA version is not correct" << std::endl;
		WSACleanup();
		return -1;
	}
#endif
	// 初始化OpenSSL库
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();

	// 创建事件基
	struct event_base* base = event_base_new();
	// 创建SSL上下文
	SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_client_method());

	// 加载客户端证书和私钥
	if (!SSL_CTX_use_certificate_file(ssl_ctx, CERT_FILE, SSL_FILETYPE_PEM) ||
		!SSL_CTX_use_PrivateKey_file(ssl_ctx, KEY_FILE, SSL_FILETYPE_PEM)) {
		spdlog::error("Failed to load client certificate or key"); // 记录错误日志
		return 1;
	}

	// 创建SSL对象并关联到bufferevent
	SSL* ssl = SSL_new(ssl_ctx);
	struct bufferevent* bev = bufferevent_openssl_socket_new(
		base, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE
	);

	// 设置bufferevent的回调函数
	bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
	// 启用bufferevent的读和写事件
	bufferevent_enable(bev, EV_READ | EV_WRITE);

	// 设置服务器地址信息
	struct sockaddr_in sin;
	memset(&sin, 0, sizeof(sin)); // 清零地址信息
	sin.sin_family = AF_INET; // 设置为IPv4
	sin.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置服务器IP地址
	sin.sin_port = htons(5555); // 设置服务器端口

	// 尝试连接到服务器
	if (bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		spdlog::critical("Failed to connect to server: {}", strerror(errno)); // 记录严重错误日志
		bufferevent_free(bev); // 释放bufferevent资源
		return 1;
	}

	spdlog::info("Client started"); // 记录客户端启动日志
	// 进入事件循环
	event_base_dispatch(base);

	// 清理资源
	event_base_free(base);
#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}



